﻿
using System;
using System.Text;
using System.Text.RegularExpressions;
using CodingMuscles.Text.Regexp;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Test.Regexp
{
    [TestClass]
    public class RegexPatternBuilderTests
    {
        [TestMethod]
        public void TestSpecialCharactersAreEscaped()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("\n\t\v\f\b\a\r$");

            Assert.AreEqual(builder.ToString(), "\\n\\t\\v\\f\\b\\a\\r\\$");

            builder = new RegexPatternBuilder();
            builder.Match("[]{}\\");

            Assert.AreEqual(builder.ToString(), "\\[\\]\\{\\}\\\\");

            builder = new RegexPatternBuilder();
            builder.Match("+$()|*");

            Assert.AreEqual(builder.ToString(), "\\+\\$\\(\\)\\|\\*");
        }

        [TestMethod]
        public void TestCharacterSetIsEscaped()
        {
            var builder = new RegexPatternBuilder().NotMatch(c => c.Include("^"));
            Assert.AreEqual(builder.ToString(), "[^^]");

            builder = new RegexPatternBuilder().Match(c => c.Include("^"));
            Assert.AreEqual(builder.ToString(), "[\\^]");

            builder = new RegexPatternBuilder().Match(c => c.Include('-'));
            Assert.AreEqual(builder.ToString(), "[-]");

            builder = new RegexPatternBuilder().Match(c => c.Include("-9"));
            Assert.AreEqual(builder.ToString(), "[-9]");

            builder = new RegexPatternBuilder().Match(c => c.Include('9').Include('-'));
            Assert.AreEqual(builder.ToString(), "[9-]");

            builder = new RegexPatternBuilder().NotMatch(c => c.Include("-9"));
            Assert.AreEqual(builder.ToString(), "[^-9]");

            builder = new RegexPatternBuilder().Match(c => c.Include("9-9"));
            Assert.AreEqual(builder.ToString(), "[9\\-9]");
        }

        [TestMethod]
        public void TestDontRequireEscape()
        {
            var builder = new RegexPatternBuilder().Match(c => c.Include("+$()|*"));
            Assert.AreEqual(builder.ToString(), "[+$()|*]");
        }

        [TestMethod]
        public void TestStartOfLineAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(Anchor.StartOfLine).Match("123");

            Assert.IsFalse(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("\n123", builder.ToString(), RegexOptions.Multiline));
        }

        [TestMethod]
        public void TestStartOfStringAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(Anchor.StartOfString).Match("123");

            Assert.IsFalse(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("\n123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123", builder.ToString()));
        }

        [TestMethod]
        public void TestEndOfLineAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("123").Match(Anchor.EndOfLine);

            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123 ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123\n", builder.ToString()));
        }

        [TestMethod]
        public void TestEndOfStringAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("123").Match(Anchor.EndOfString);

            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123 ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123\n", builder.ToString()));
        }

        [TestMethod]
        public void TestEndOfStringAndNewlineAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("123").Match(Anchor.EndOfStringAndNewline);

            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123 ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123\n", builder.ToString()));
        }

        [TestMethod]
        public void TestStartWordBoundaryAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(Anchor.WordBoundary).Match("123");

            Assert.IsTrue(Regex.IsMatch("456 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(";123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("4123", builder.ToString()));
        }

        [TestMethod]
        public void TestEndWordBoundaryAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("123").Match(Anchor.WordBoundary);

            Assert.IsTrue(Regex.IsMatch("456 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123;", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1234", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123 ", builder.ToString()));
        }

        [TestMethod]
        public void TestWordBoundaryAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(Anchor.WordBoundary).Match("123").Match(Anchor.WordBoundary);

            Assert.IsTrue(Regex.IsMatch("456 123 789", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123;", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1234", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("X1234", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("X123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123 ", builder.ToString()));
        }

        [TestMethod]
        public void TestNonWordBoundaryAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(Anchor.NonWordBoundary).Match("123").Match(Anchor.NonWordBoundary);

            Assert.IsFalse(Regex.IsMatch("456 123 789", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123;", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1234", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("_123_", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("X1234", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("X123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123 ", builder.ToString()));
        }

        [TestMethod]
        public void TestLastMatchAnchor()
        {
            var builder = new RegexPatternBuilder();
            builder
                .Match(Anchor.PreviousMatch)
                .Group(g => g.Match(CharacterClass.Word, 1, Quantifier.OrMore)
                    .Match(CharacterClass.Whitespace, 0, 1)
                    .Match(CharacterClass.Word, 0, Quantifier.OrMore))
                .Match(',', 0, 1);

            var match = Regex.Match("dog,mouse,prairie dog", builder.ToString());
            Assert.IsTrue(match.Success);
            Assert.AreEqual("dog", match.Groups[1].Value);

            match = match.NextMatch();
            Assert.IsTrue(match.Success);
            Assert.AreEqual("mouse", match.Groups[1].Value);

            match = match.NextMatch();
            Assert.IsTrue(match.Success);
            Assert.AreEqual("prairie dog", match.Groups[1].Value);

            match = match.NextMatch();
            Assert.IsFalse(match.Success);
        }

        [TestMethod]
        public void TestMatchLiteralString()
        {
            var builder = new RegexPatternBuilder();
            builder.Match("abc");

            Assert.IsTrue(Regex.IsMatch("abc", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("def", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a bc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("  abc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abc  ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abcabc", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchNumericValue()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(3);

            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3}), builder.ToString()));
            Assert.AreEqual(builder.ToString(), "\\x03");
        }

        [TestMethod]
        public void TestMatchLargeNumericValue()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(1024);

            Assert.IsTrue(Regex.IsMatch(Encoding.Unicode.GetString(BitConverter.GetBytes(1024)), builder.ToString()));
            Assert.AreEqual(builder.ToString(), "\\u0400");
        }

        [TestMethod]
        public void TestMatchNumericValueQualifedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(3, 1, Quantifier.OrMore);

            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3}), builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {5}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {5, 3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 3, 3, 3, 3, 3, 3, 3}),
                builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Match(3, 1);

            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3}), builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {5}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {5, 3, 3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 3}), builder.ToString()));
        }

        [TestMethod]
        public void TestMatchNumericValueMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(3, 2, 4);

            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 3, 3, 3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 3, 3, 3, 3}), builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3, 4, 3, 5, 3}), builder.ToString()));
        }

        [TestMethod]
        public void TestCharacterClassesAreRepresented()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(CharacterClass.Any)
                .Match(CharacterClass.Digit)
                .Match(CharacterClass.NonDigit)
                .Match(CharacterClass.NonWhitespace)
                .Match(CharacterClass.NonWord)
                .Match(CharacterClass.Word)
                .Match(CharacterClass.Whitespace);

            Assert.AreEqual(builder.ToString(), ".\\d\\D\\S\\W\\w\\s");
        }

        [TestMethod]
        public void TestMatchCharacterClass()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(CharacterClass.Any)
                .Match(CharacterClass.Digit)
                .Match(CharacterClass.NonDigit)
                .Match(CharacterClass.NonWhitespace)
                .Match(CharacterClass.NonWord)
                .Match(CharacterClass.Word)
                .Match(CharacterClass.Whitespace);

            Assert.IsTrue(Regex.IsMatch("x9az g ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("xtaz g ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("x96z g ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("x9a  g ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("x9az4g ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("x9az   ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("x9az gz", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchCharacterClassQualifedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(CharacterClass.Digit, 3, Quantifier.OrMore);

            Assert.AreEqual(builder.ToString(), "\\d{3,}");
            Assert.IsFalse(Regex.IsMatch("90x54", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("5", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 555", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("667545", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("33.3", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("567.2", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Match(CharacterClass.Digit, 2);

            Assert.AreEqual(builder.ToString(), "\\d{2}");
            Assert.IsTrue(Regex.IsMatch("90x54", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("5", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 555", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("667545", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("33.3", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("2x2", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchCharacterClassMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(CharacterClass.Word, 2, 4);

            Assert.AreEqual(builder.ToString(), "\\w{2,4}");
            Assert.IsFalse(Regex.IsMatch("w", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("wo", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("words", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("w or d s", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("w o r d s", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchLiteralChar()
        {
            var builder = new RegexPatternBuilder();
            builder.Match('c');

            Assert.IsFalse(Regex.IsMatch("x", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("c", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" c", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("C", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchLiteralCharQualifedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match('c', 2, Quantifier.OrMore);

            Assert.IsFalse(Regex.IsMatch("c", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("ccc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" cc x", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("C c c", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Match('c', 3);

            Assert.IsFalse(Regex.IsMatch("x", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("c ccc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cccd", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" xcccx", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("Ccc", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchLiteralCharMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match('c', 2, 4);

            Assert.IsTrue(Regex.IsMatch("ccccx", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc cccc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" C ccc", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("cx cx", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("CCC", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchCharacterSet()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(
                c => c.Include('4', '6').Include('z').Include(CharacterClass.Whitespace).Include(3).Include('-'));

            Assert.AreEqual(builder.ToString(), "[4-6z\\s\\x03-]");
            Assert.IsTrue(Regex.IsMatch("4", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("5", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("6", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("z", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {3}), builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("44", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("5x", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("x6", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("x-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("zxxx", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("    ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("--", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("3", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("7", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("j", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(";", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(Encoding.ASCII.GetString(new byte[] {4}), builder.ToString()));
        }

        [TestMethod]
        public void TestMatchCharacterSetQualifedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.Include(CharacterClass.Digit).Include('-'), 2, Quantifier.OrMore);

            Assert.IsTrue(Regex.IsMatch("0-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("09-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("555", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("--", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("-1", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("0", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("-a1", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("-!-", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Match(c => c.Include(CharacterClass.Digit).Include('-'), 3);

            Assert.IsTrue(Regex.IsMatch("0-0", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("09-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("5556", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("---", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("--1", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("23", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("23 -", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" 9a0", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" ", builder.ToString()));
        }

        [TestMethod]
        public void TestMatchCharacterSetMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.Include(CharacterClass.Digit).Include('a', 'c'), 2, 4);

            Assert.IsTrue(Regex.IsMatch("00", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("09a", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("555", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("bb", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("b4", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("ddd", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a b c 0", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("  ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1 1", builder.ToString()));
        }

        [TestMethod]
        public void TestNotMatchCharacterSet()
        {
            var builder = new RegexPatternBuilder();
            builder.NotMatch(c => c.Include('a', 'd').Include(CharacterClass.Whitespace));

            Assert.AreEqual(builder.ToString(), "[^a-d\\s]");
            Assert.IsTrue(Regex.IsMatch("e", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("fgh123;(){}", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("_111", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("a", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("b", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("c ", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" ", builder.ToString()));
        }

        [TestMethod]
        public void TestNotMatchCharacterSetQualifedInstances()
        {
            var builder = new RegexPatternBuilder().NotMatch(c => c.Include(CharacterClass.Digit).Include('-'), 2,
                Quantifier.OrMore);

            Assert.AreEqual(builder.ToString(), "[^\\d-]{2,}");
            Assert.IsTrue(Regex.IsMatch("aa-", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("-aa", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc3dd", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("  ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abcde", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("4  4  5  5", builder.ToString()));

            builder = new RegexPatternBuilder().NotMatch(c => c.Include(CharacterClass.Digit).Include('-'), 3);

            Assert.AreEqual(builder.ToString(), "[^\\d-]{3}");
            Assert.IsFalse(Regex.IsMatch("aa3", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("-66", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch(" 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("0-0", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a-a", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("  ", builder.ToString()));
        }

        [TestMethod]
        public void TestNotMatchCharacterSetMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.NotMatch(c => c.Include(CharacterClass.Digit).Include('a', 'c'), 2, 4);

            Assert.AreEqual(builder.ToString(), "[^\\da-c]{2,4}");
            Assert.IsTrue(Regex.IsMatch("ddd", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("a  b  c  0", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("  ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1  1", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("00", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("09a", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("555", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("bb", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("b4", builder.ToString()));
        }

        [TestMethod]
        public void TestGroup()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"));

            Assert.AreEqual(builder.ToString(), "(123)");

            Assert.IsTrue(Regex.IsMatch("abc123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 123 ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123abc", builder.ToString()));

            var match = Regex.Match("123123123", builder.ToString());
            Assert.IsTrue(match.Success);
            Assert.AreEqual(match.Groups[1].Value, "123");

            Assert.IsFalse(Regex.IsMatch("12", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1 23", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1 233", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("23", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupQualifiedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"), 2, Quantifier.OrMore);

            Assert.AreEqual(builder.ToString(), "(123){2,}");
            Assert.IsTrue(Regex.IsMatch("123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("11231233", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123123123", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("12312", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("321321", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("23", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"), 3);

            Assert.AreEqual(builder.ToString(), "(123){3}");
            Assert.IsTrue(Regex.IsMatch("123123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("11231231233", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123123123123123", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123 123 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("12312312", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("321321321", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("232323", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"), 3, 4);

            Assert.AreEqual(builder.ToString(), "(123){3,4}");
            Assert.IsTrue(Regex.IsMatch("123123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123123123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123 123123123", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("12312312 3", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupNoCapture()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"), false);

            Assert.AreEqual(builder.ToString(), "(?:123)");

            Assert.IsTrue(Regex.IsMatch("abc123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch(" 123 ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123abc", builder.ToString()));

            var match = Regex.Match("123123123", builder.ToString());
            Assert.IsTrue(match.Success);
            // count would be 2 without false capture
            Assert.AreEqual(match.Groups.Count, 1);

            Assert.IsFalse(Regex.IsMatch("12", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1 23", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("1 233", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("23", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupNoCaptureQualifiedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("abc"), 5, Quantifier.OrMore, false);

            Assert.AreEqual(builder.ToString(), "(?:abc){5,}");
            Assert.IsTrue(Regex.IsMatch("abcabcabcabcabc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abcabcabcabcabcabc", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abcabcabcabcabc abc", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("abcabcabcabc", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("abc abc abc abc abc", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("abcabcabcabcab", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("xyz"), 5, Quantifier.Exactly, false);

            Assert.IsTrue(Regex.IsMatch("xyzxyzxyzxyzxyz", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("xyzxyzxyzxyzxyzyxz", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("xyzxyzxyzxyzxyz xyz", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("xyzxyzxyzxyz", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("xyz xyz xyz xyz xyz", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("xyzxyzxyzxyzxy", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupNoCaptureMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group(g => g.Match("123"), 2, 5, false);

            Assert.AreEqual(builder.ToString(), "(?:123){2,5}");
            Assert.IsTrue(Regex.IsMatch("123123123123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123123", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1 123123", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("12312", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123 123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("11223", builder.ToString()));
        }

        [TestMethod]
        public void TestNamedGroup()
        {
            var builder = new RegexPatternBuilder();
            builder
                .Group("co_code", g => g.Match(CharacterClass.Digit, 3))
                .Match('-')
                .Group("number", g => g.Match(CharacterClass.Digit, 4));

            Assert.AreEqual(builder.ToString(), "(?<co_code>\\d{3})-(?<number>\\d{4})");
            Assert.IsTrue(Regex.IsMatch("123-4567", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("2234-4567", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123-45677", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("a34-4567", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("134x4567", builder.ToString()));

            var match = Regex.Match("345-6789", builder.ToString());
            Assert.IsTrue(match.Success);
            Assert.AreEqual(match.Groups["co_code"].Value, "345");
            Assert.AreEqual(match.Groups["number"].Value, "6789");
        }

        [TestMethod]
        public void TestNamedGroupQualifiedInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group("test", g => g.Match(CharacterClass.Digit, 3), 2, Quantifier.OrMore);

            Assert.IsTrue(Regex.IsMatch("123456", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1234567", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123456789", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123-456", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a34-4567", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a12345", builder.ToString()));

            builder = new RegexPatternBuilder();
            builder.Group("test", g => g.Match(CharacterClass.Digit, 3), 3, Quantifier.OrMore);

            Assert.IsTrue(Regex.IsMatch("123456789", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("12345678910", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abc123456789abc", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123456", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("12345678", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a12345678", builder.ToString()));
        }

        [TestMethod]
        public void TestNamedGroupMinMaxInstances()
        {
            var builder = new RegexPatternBuilder();
            builder.Group("test", g => g.Match(CharacterClass.Digit, 3), 2, 4);

            Assert.IsTrue(Regex.IsMatch("123456", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("aaa123aaa456789aaa", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("123456789012345", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("123", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("123-456", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a34-4567", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("a12345", builder.ToString()));
        }

        [TestMethod]
        public void TestAlternateLiterals()
        {
            var builder = new RegexPatternBuilder();
            builder.Alternate(l => l.Match("abc"), r => r.Match("123"));

            Assert.AreEqual(builder.ToString(), "abc|123");
            Assert.IsTrue(Regex.IsMatch("abcdef", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("1123456", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abd 1233", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("abc 124", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("12a", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("abd-124", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("", builder.ToString()));
        }

        [TestMethod]
        public void TestAlternateGroups()
        {
            var builder = new RegexPatternBuilder();
            builder.Alternate(l => l.Group(b => b.Match(c => c.Include('a', 'd')).Match(CharacterClass.Whitespace)),
                r => r.Group(b => b.Match(c => c.Include('e', 'g')).Match(CharacterClass.Digit)));

            Assert.AreEqual(builder.ToString(), "([a-d]\\s)|([e-g]\\d)");
            Assert.IsTrue(Regex.IsMatch("a ", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("b\t", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("dd\n", builder.ToString()));
            Assert.IsTrue(Regex.IsMatch("cc\r\r", builder.ToString()));

            Assert.IsFalse(Regex.IsMatch("cc", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("\n\n", builder.ToString()));
            Assert.IsFalse(Regex.IsMatch("c4\n", builder.ToString()));
        }

        [TestMethod]
        public void TestGroupNesting()
        {
            var builder = new RegexPatternBuilder();
            builder.Alternate(
                l => l.Group(g => g.Group(g2 => g2.Group(g3 => g3.Group("blah", g4 => g4.Match("1234567"))), false)),
                r =>
                    r.Group(
                        g =>
                            g.Alternate(
                                l => l.Group("another", g5 => g5.Group(g6 => g6.Match("abcd"), 1, Quantifier.OrMore)),
                                x => x.Match("!@#$%^&*()_+").Match(c => c.Include("^^")))));

            Assert.AreEqual(builder.ToString(),
                "((?:((?<blah>1234567))))|((?<another>(abcd)+)|!@#\\$%\\^&\\*\\(\\)_\\+[\\^^])");

        }

        [TestMethod]
        public void TestCharacterSetRangeReverseOrder()
        {
            var builder = new RegexPatternBuilder();

            try
            {
                builder.Match(c => c.Include('z', 'a'));
                Assert.IsTrue(false);
            }
            catch (ArgumentException)
            {
            }
            catch (Exception)
            {
                Assert.IsTrue(false);
            }
        }

        [TestMethod]
        public void TestCharacterSetRangeEqual()
        {
            var builder = new RegexPatternBuilder();

            builder.Match(c => c.Include('a', 'a'));

            Assert.AreEqual(builder.ToString(), "[a]");
        }

        [TestMethod]
        public void TestUnicodeCharacterSet()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.IncludeUnicodeCategory("P"));

            Assert.AreEqual(builder.ToString(), "[\\p{P}]");
            Assert.IsTrue(Regex.IsMatch(",", builder.ToString()));
        }

        [TestMethod]
        public void TestNotUnicodeCharacterSet()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.ExcludeUnicodeCategory("P"));

            Assert.AreEqual(builder.ToString(), "[\\P{P}]");
            Assert.IsFalse(Regex.IsMatch(",", builder.ToString()));
        }

        [TestMethod]
        public void TestCharacterSetWithLargeNumericValue()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.Include(1024));

            Assert.IsTrue(Regex.IsMatch(Encoding.Unicode.GetString(BitConverter.GetBytes(1024)), builder.ToString()));
            Assert.AreEqual(builder.ToString(), "[\\u0400]");
        }

        [TestMethod]
        public void TestAddComment()
        {
            var builder = new RegexPatternBuilder();
            builder.Comment("this is a test");

            Assert.AreEqual(builder.ToString(), "(?#this is a test)");
        }

        [TestMethod]
        public void TestQualifierRangeInverted()
        {
            var builder = new RegexPatternBuilder();

            try
            {
                builder.Match(c => c.Include('z', 'a'));
                Assert.IsTrue(false);
            }
            catch (ArgumentException)
            {
            }
            catch (Exception)
            {
                Assert.IsTrue(false);
            }
        }

        [TestMethod]
        public void TestQualifierRangeEqual()
        {
            var builder = new RegexPatternBuilder();
            builder.Match(c => c.Include('a'), 3, 3);

            Assert.AreEqual(builder.ToString(), "[a]{3}");

            builder.Match(c => c.Include('b'), 3, 4);

            Assert.AreEqual(builder.ToString(), "[a]{3}[b]{3,4}");
        }
    }
}

